package jvm.VMExecSubsystem;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;

import static java.lang.invoke.MethodHandles.lookup;

/**
 * JSR 292 MethodHandle基础用法演示
 * @author msi
 */
public class MethodHandleTest {

    static class ClassA {
        public void println(String s) {
            System.out.println(s);
        }
    }

    public static void main(String[] args) throws Throwable {
        Object obj = System.currentTimeMillis() % 2 == 0 ? System.out : new ClassA();
        // 无论obj最终是哪个实现类，下面这句都能正确调用到println方法。
        getPrintlnMH(obj).invokeExact("icyfenix");
        //反射调用println的方法
        obj.getClass().getMethod("println",String.class).invoke(obj,"reflection");
    }

    private static MethodHandle getPrintlnMH(Object reveiver) throws Throwable {
        // MethodType：代表“方法类型”，包含了方法的返回值（methodType()的第一个参数）和具体参数（methodType()第二个及以后的参数）。
        MethodType mt = MethodType.methodType(void.class, String.class);
        // lookup()方法来自于MethodHandles.lookup，这句的作用是在指定类中查找符合给定的方法名称、方法类型，并且符合调用权限的方法句柄。
        // 因为这里调用的是一个虚方法，按照Java语言的规则，方法第一个参数是隐式的，代表该方法的接收者，也即是this指向的对象，这个参数以前是放在参数列表中进行传递，现在提供了bindTo()方法来完成这件事情。
        return lookup().findVirtual(reveiver.getClass(), "println", mt).bindTo(reveiver);
    }
}
/**
 * MethodHandle 方法句柄和 Reflection 反射的区别：
 *      上述功能其实通过反射一样可以实现,仅仅从java角度来看MethodHandle 和Reflection 确实结果相似，但是它们也有这几个不同。
 *      1.MethodHandle 和 Reflection 本质上都是在模拟方法调用，但是Reflection是在模拟java代码层次的方法调用，而MethodHandle是在模拟字节码层次的方法调用，在Method.Lookup 上的三个方法
 *          findStatic(),findVirtual(),findSpecial() 正是为了对应 invokestatic,invokevirtual,invokespecial指令。而这些底层细节在Reflection API 中则不用关系这些。
 *      2.Reflection 中的 java.lang.reflect.Method 对象远比 MethodHandle 机制中的 java.lang.invoke.MethodHandle 对象所包含的信息来的多，前者是方法在java端的全面映像，包含了方法的签名，描述符，以及方法属性表
 *          中的各种属性，还包括权限信息等。而后者仅仅包含执行该方法的信息， 通俗的说，Reflection是重量级的，MethodHandle是轻量级的。
 *      3.由于MethodHandle是对字节码的方法指令的模拟，所以也包含虚拟机对方法的一些优化措施，比如方法内联。在MethodHandle中也应该以相同的思路去实现(尽管现在还没有实现)，但是这些功能是Reflection不关心的。
 *      4.Reflection API的设计仅仅是为了java语言服务的，但是MethodHandle的设计理念是为了所有能在java虚拟机中执行的语言所实现的，
 */
